package trip

import (
	"context"
	"fmt"
	"go.uber.org/zap"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"math/rand"
	rentalpb "serve/rental/api/gen/v1"
	"serve/rental/trip/dao"
	"serve/shared/antu/auth"
	"serve/shared/id"
	"serve/shared/mongo/objid"
	"time"
)

type Service struct {
	ProfileManager ProfileManager
	CarManager     CarManager
	POIManager     POIManager
	Logger         *zap.Logger
	Mongo          *dao.Mongo
}

//ProfileManager 资格认证接口
type ProfileManager interface {
	Verify(context.Context, id.AccountID) (id.IdentityID, error)
}
type CarManager interface {
	Verify(context.Context, id.CarID, *rentalpb.Location) error
	Unlock(context.Context, id.CarID, id.AccountID, id.TripID, string) error
	Lock(context.Context, id.CarID) error
}

//POIManager 根据经纬度查询地标
type POIManager interface {
	Resolve(ctx context.Context, location *rentalpb.Location) (string, error) // 查询地标
}

//CreateTrip  创建行程
func (s *Service) CreateTrip(c context.Context, request *rentalpb.CreateTripRequest) (*rentalpb.TripEntity, error) {
	// 获取用户id
	aid, err := auth.AccountIDFromContext(c)
	if err != nil {
		return nil, err
	}

	if request.CarId == "" || request.Start == nil {
		return nil, status.Errorf(codes.InvalidArgument, "")
	}

	// 验证驾驶者身份
	IdentityID, err := s.ProfileManager.Verify(c, aid)
	fmt.Println(IdentityID)
	if err != nil {
		return nil, status.Errorf(codes.FailedPrecondition, err.Error())
	}

	//检查车辆状态
	carID := id.CarID(request.CarId)
	err = s.CarManager.Verify(c, carID, request.Start)
	if err != nil {
		return nil, status.Errorf(codes.FailedPrecondition, err.Error())
	}

	ls := s.calcCurrentStatus(c, &rentalpb.LocationStatus{
		Location:     request.Start,
		TimestampSec: nowFun(),
	}, request.Start)

	var currentTs []*rentalpb.LocationStatus
	currentTs = append(currentTs, ls)
	//创建行程
	trip, err := s.Mongo.CreateTrip(c, &rentalpb.Trip{
		AccountId:  aid.String(),
		CarId:      carID.String(),
		IdentityId: IdentityID.String(),
		Start:      ls,
		Currents:   currentTs,
		Status:     rentalpb.TripStatus_IN_PROGRESS,
	})
	if err != nil {
		s.Logger.Warn("创建行程失败", zap.Error(err))
		return nil, status.Error(codes.AlreadyExists, "创建行程失败")
	}

	//车辆开锁
	go func() {
		err := s.CarManager.Unlock(context.Background(), carID, aid, objid.ToTripID(trip.ID), request.AvatarUrl)
		if err != nil {
			s.Logger.Error("开锁失败", zap.Error(err))
		}
	}()

	return &rentalpb.TripEntity{
		Id:   trip.ID.Hex(),
		Trip: trip.Trip,
	}, nil
}

func (s *Service) GetTrip(ctx context.Context, request *rentalpb.GetTripRequest) (*rentalpb.Trip, error) {
	// 获取用户id
	aid, err := auth.AccountIDFromContext(ctx)
	if err != nil {
		return nil, err
	}

	trp, err := s.Mongo.GetTrip(ctx, id.TripID(request.Id), aid)
	if err != nil {
		s.Logger.Error("trip get err:%v", zap.Error(err))
		return nil, status.Errorf(codes.NotFound, "获取行程失败")
	}

	return trp.Trip, nil
}

func (s *Service) GetTrips(ctx context.Context, request *rentalpb.GetTripsRequest) (*rentalpb.GetTripsResponse, error) {
	// 获取用户id
	aid, err := auth.AccountIDFromContext(ctx)
	if err != nil {
		return nil, err
	}

	trips, err := s.Mongo.GetTrips(ctx, aid, request.Status)
	if err != nil {
		s.Logger.Error("cannot get trips err:%v", zap.Error(err))
		return nil, status.Errorf(codes.Internal, "获取行程失败")
	}

	var res rentalpb.GetTripsResponse
	for _, tr := range trips {
		res.Trips = append(res.Trips, &rentalpb.TripEntity{
			Id:   tr.ID.Hex(),
			Trip: tr.Trip,
		})
	}
	return &res, nil
}

func (s *Service) UpdateTrip(ctx context.Context, request *rentalpb.UpdateTripRequest) (*rentalpb.Trip, error) {
	aid, err := auth.AccountIDFromContext(ctx)
	if err != nil {
		return nil, status.Errorf(codes.Unauthenticated, "获取身份信息失败")
	}
	tid := id.TripID(request.Id)
	tr, err := s.Mongo.GetTrip(ctx, tid, aid)
	if err != nil {
		return nil, status.Errorf(codes.NotFound, "获取行程失败")
	}

	if tr.Trip.Currents == nil {
		s.Logger.Error("trip without current set", zap.String("id", tid.String()))
		return nil, status.Errorf(codes.Internal, "")
	}

	var current *rentalpb.LocationStatus
	current = tr.Trip.Currents[len(tr.Trip.Currents)-1]
	//fmt.Println(current)
	cur := current.Location
	if request.Current != nil {
		cur = request.Current
	}

	tr.Trip.Currents = append(tr.Trip.Currents, s.calcCurrentStatus(ctx, current, cur))

	if request.EndTrip {
		tr.Trip.End = current
		tr.Trip.Status = rentalpb.TripStatus_FINISHED

		err := s.CarManager.Lock(ctx, id.CarID(tr.Trip.CarId)) //关锁
		if err != nil {
			return nil, status.Errorf(codes.FailedPrecondition, "关锁失败%v", err)
		}
	}

	err = s.Mongo.UpdateTrip(ctx, tid, aid, tr.UpdatedAt, tr.Trip)
	if err != nil {
		return nil, status.Errorf(codes.Unauthenticated, "更新行程失败")
	}
	return tr.Trip, nil
}

var nowFun = func() int64 {
	return time.Now().Unix()
}

const centsPerSec = 0.7
const kenPerSec = 0.2

func (s *Service) calcCurrentStatus(ctx context.Context, last *rentalpb.LocationStatus, cur *rentalpb.Location) *rentalpb.LocationStatus {
	timestampSec := nowFun()
	elapsedSec := float64(timestampSec - last.TimestampSec)

	poi, err := s.POIManager.Resolve(ctx, &rentalpb.Location{
		Latitude:  cur.Latitude,
		Longitude: cur.Longitude,
	})
	if err != nil {
		s.Logger.Info("s.POIManager.Resolve(c, request.Start) 获取地标失败", zap.Stringer("location", cur), zap.Error(err))
	}
	return &rentalpb.LocationStatus{
		Location:     cur,
		FeeCent:      last.FeeCent + int32(centsPerSec*elapsedSec*2*rand.Float64()),
		KmDriven:     last.KmDriven + kenPerSec*elapsedSec*2*rand.Float64(),
		PoiName:      poi,
		TimestampSec: timestampSec,
	}
}
